import datetime

from criticalpath import Node

from osc04a.domain.activities.AssemblingGrouted import AssemblingGrouted
from osc04a.domain.activities.Grout import Grout
from osc04a.domain.activities.StandardFloorErection import StandardFloorErection
from osc04a.static.constants import Constants
from osc04a.utils.cpm_class import ProjectScheduler


def init_project():
    # virtual activities, START and END are removed
    onsite_activities = []
    first_floor_no = 101
    # create the floors 1 which need more days to complete
    for i in range(1):
        standard_floor_erection = StandardFloorErection(
            name=('FLR' + str(i + first_floor_no).zfill(len(str(first_floor_no)))[1:]),
            duration_likely=Constants.STANDARD_FLOOR_ERECTION_MODE + 1,
            duration_low=Constants.STANDARD_FLOOR_ERECTION_LEFT + 1,
            duration_high=Constants.STANDARD_FLOOR_ERECTION_RIGHT + 1,
            worker_skill='1', worker_num=5,
            crane_type='1'
        )
        # set all durations to 7
        standard_floor_erection.duration = 7
        onsite_activities.append(standard_floor_erection)

    first_floor_no = 102
    n_standard_floor = Constants.N_STD_FLR
    for i in range(n_standard_floor):
        standard_floor_erection = \
            StandardFloorErection(name=('FLR' + str(i + first_floor_no).zfill(len(str(first_floor_no)))[1:]),
                                  duration_likely=Constants.STANDARD_FLOOR_ERECTION_MODE,
                                  duration_low=Constants.STANDARD_FLOOR_ERECTION_LEFT,
                                  duration_high=Constants.STANDARD_FLOOR_ERECTION_RIGHT,
                                  worker_skill='1', worker_num=5,
                                  crane_type='1'
                                  )
        standard_floor_erection.duration = 6
        onsite_activities.append(standard_floor_erection)
    # create the other floors which need more time to complete
    first_floor_no = 118;
    standard_floor_erection = StandardFloorErection(
        name=('FLR' + str(first_floor_no).zfill(len(str(first_floor_no)))[1:]),
        duration_likely=Constants.STANDARD_FLOOR_ERECTION_MODE + 1,
        duration_low=Constants.STANDARD_FLOOR_ERECTION_LEFT + 1,
        duration_high=Constants.STANDARD_FLOOR_ERECTION_RIGHT + 1,
        worker_skill='1', worker_num=5,
        crane_type='1'
    )
    # set the floor's duration to 7
    standard_floor_erection.duration = 7
    onsite_activities.append(standard_floor_erection)

    p = Node('project')

    for activity in onsite_activities:
        p.add(activity)
    # set the no. 0-47's precedence according to the number
    p.link(onsite_activities[0], onsite_activities[1]) \
        .link(onsite_activities[1], onsite_activities[2]) \
        .link(onsite_activities[2], onsite_activities[3]) \
        .link(onsite_activities[3], onsite_activities[4]) \
        .link(onsite_activities[4], onsite_activities[5]) \
        .link(onsite_activities[5], onsite_activities[6]) \
        .link(onsite_activities[6], onsite_activities[7]) \
        .link(onsite_activities[7], onsite_activities[8]) \
        .link(onsite_activities[8], onsite_activities[9]) \
        .link(onsite_activities[9], onsite_activities[10]) \
        .link(onsite_activities[10], onsite_activities[11]) \
        .link(onsite_activities[11], onsite_activities[12]) \
        .link(onsite_activities[12], onsite_activities[13]) \
        .link(onsite_activities[13], onsite_activities[14]) \
        .link(onsite_activities[14], onsite_activities[15]) \
        .link(onsite_activities[15], onsite_activities[16]) \
        .link(onsite_activities[16], onsite_activities[17]) \
        # .link(onsite_activities[17], onsite_activities[18]) \
    # .link(onsite_activities[18], onsite_activities[19]) \
    # .link(onsite_activities[19], onsite_activities[20]) \
    # .link(onsite_activities[20], onsite_activities[21]) \
    # .link(onsite_activities[21], onsite_activities[22]) \
    # .link(onsite_activities[22], onsite_activities[23]) \
    # .link(onsite_activities[23], onsite_activities[24]) \
    # .link(onsite_activities[24], onsite_activities[25]) \
    # .link(onsite_activities[25], onsite_activities[26]) \
    # .link(onsite_activities[26], onsite_activities[27]) \
    # .link(onsite_activities[27], onsite_activities[28]) \
    # .link(onsite_activities[28], onsite_activities[29]) \
    # .link(onsite_activities[29], onsite_activities[30]) \
    # .link(onsite_activities[30], onsite_activities[31]) \
    # .link(onsite_activities[31], onsite_activities[32]) \
    # .link(onsite_activities[32], onsite_activities[33]) \
    # .link(onsite_activities[33], onsite_activities[34]) \
    # .link(onsite_activities[34], onsite_activities[35]) \
    # .link(onsite_activities[35], onsite_activities[36]) \
    # .link(onsite_activities[36], onsite_activities[37]) \
    # .link(onsite_activities[37], onsite_activities[38]) \
    # .link(onsite_activities[38], onsite_activities[39]) \
    # .link(onsite_activities[39], onsite_activities[40]) \
    # .link(onsite_activities[40], onsite_activities[41]) \
    # .link(onsite_activities[41], onsite_activities[42]) \
    # .link(onsite_activities[42], onsite_activities[43]) \
    # .link(onsite_activities[43], onsite_activities[44]) \
    # .link(onsite_activities[44], onsite_activities[45]) \
    # .link(onsite_activities[45], onsite_activities[46]) \
    # .link(onsite_activities[46], onsite_activities[47])
    #     .link(onsite_activities[47], onsite_activities[48]) \
    #     .link(onsite_activities[48], onsite_activities[49]) \
    #     .link(onsite_activities[49], onsite_activities[50]) \
    #     .link(onsite_activities[50], onsite_activities[51]) \
    #     .link(onsite_activities[51], onsite_activities[52]) \
    #     .link(onsite_activities[52], onsite_activities[53]) \
    #     .link(onsite_activities[53], onsite_activities[54]) \
    #     .link(onsite_activities[54], onsite_activities[55]) \
    #     .link(onsite_activities[55], onsite_activities[56]) \
    #     .link(onsite_activities[56], onsite_activities[57]) \
    #     .link(onsite_activities[57], onsite_activities[58]) \
    #     .link(onsite_activities[58], onsite_activities[59]) \
    #     .link(onsite_activities[59], onsite_activities[60]) \
    #     .link(onsite_activities[60], onsite_activities[61]) \
    #     .link(onsite_activities[61], onsite_activities[62]) \
    #     .link(onsite_activities[62], onsite_activities[63]) \
    #     .link(onsite_activities[63], onsite_activities[64]) \
    #     .link(onsite_activities[64], onsite_activities[65]) \
    #     .link(onsite_activities[65], onsite_activities[66]) \
    #     .link(onsite_activities[66], onsite_activities[67]) \
    #     .link(onsite_activities[67], onsite_activities[68]) \
    #     .link(onsite_activities[68], onsite_activities[69]) \
    #     .link(onsite_activities[69], onsite_activities[70]) \
    #     .link(onsite_activities[70], onsite_activities[71]) \
    #     .link(onsite_activities[71], onsite_activities[72]) \
    #     .link(onsite_activities[72], onsite_activities[73]) \
    #     .link(onsite_activities[73], onsite_activities[74]) \
    #     .link(onsite_activities[74], onsite_activities[75]) \
    #     .link(onsite_activities[75], onsite_activities[76]) \
    #     .link(onsite_activities[76], onsite_activities[77]) \
    #     .link(onsite_activities[77], onsite_activities[78]) \
    #     .link(onsite_activities[78], onsite_activities[79]) \
    #     .link(onsite_activities[79], onsite_activities[80]) \
    #     .link(onsite_activities[80], onsite_activities[81]) \
    #     .link(onsite_activities[81], onsite_activities[82]) \
    #     .link(onsite_activities[82], onsite_activities[83]) \
    #     .link(onsite_activities[83], onsite_activities[84]) \
    #     .link(onsite_activities[84], onsite_activities[85]) \
    #     .link(onsite_activities[85], onsite_activities[86]) \
    #     .link(onsite_activities[86], onsite_activities[87]) \
    #     .link(onsite_activities[87], onsite_activities[88]) \
    #     .link(onsite_activities[88], onsite_activities[89]) \
    #     .link(onsite_activities[89], onsite_activities[90]) \
    #     .link(onsite_activities[90], onsite_activities[91]) \
    #     .link(onsite_activities[91], onsite_activities[92]) \
    #     .link(onsite_activities[92], onsite_activities[93]) \
    #     .link(onsite_activities[93], onsite_activities[94]) \
    #     .link(onsite_activities[94], onsite_activities[95])

    # all grouting activities are successors of vertical component assembling
    assembling_grouted_set = set()
    grout_set = set()
    for activity in onsite_activities:
        # divide activities into two sets
        if isinstance(activity, AssemblingGrouted):
            assembling_grouted_set.add(activity)
        elif isinstance(activity, Grout):
            grout_set.add(activity)
    # link all the assembling_grouted to grouts
    for assembling_grouted in assembling_grouted_set:
        for grout in grout_set:
            p.link(assembling_grouted, grout)

    return p


def get_schedule(prj_start_date: object = '2023-3-10') -> object:
    # set project's start date with the parameter
    prj_start_date = datetime.datetime.strptime(prj_start_date, '%Y-%m-%d')
    # get project
    p = init_project()
    # 给各节点赋值
    p.update_all()
    # 活动
    activities = p.nodes
    # set scheduled and actual start and finish
    # for activity in activities:
    #     activity.skd_erect_start_date = activity.es
    #     activity.skd_erect_finish_date = activity.es + activity.duration

    # activity.act_erect_start_date = activity.skd_erect_start_date
    # activity.act_erect_finish_date = activity.skd_erect_finish_date

    # activity.delivery_date = activity.skd_erect_start_date

    # GanttChart(activities, prj_start_date).show()
    return activities, prj_start_date


# update all the successors' es, actual start
def update_successors_time_parameters(activity):
    # hold current act_finish
    original_act_finish = activity.act_erect_finish_date
    # assign es to act_start
    activity.act_erect_start_date = activity.es
    # calculate act_finish
    activity.act_erect_finish_date = activity.act_erect_start_date + activity.act_duration

    # if new actual finish is not later than the original, return.
    if activity.act_erect_finish_date <= original_act_finish:
        # logging.debug(activity.name + ', act_finish is no later than original, no need to update the successors')
        return

    # if the current activity is the ending activity, return.
    successors = activity.to_nodes
    if len(successors) == 0:
        # logging.debug(activity.name + ', having no successors, return')
        return

    # activity.act_start
    # logging.debug(activity.name + ', actual finish is later, need to delay more successors')
    # An activity's es would be influenced by all predecessors' act_finish.
    # So, only when a predecessor's act_finish is later than the activity' es,
    # the latter's es should be overwritten
    for successor in successors:
        # if activity's actual finish is later than the successor's es
        if activity.act_erect_finish_date > successor.es:
            successor.es = activity.act_erect_finish_date
        # go to update more with a deeper recursion
        update_successors_time_parameters(successor)


if __name__ == '__main__':
    activities, prj_start_date = get_schedule()
    # 打印活动名称和计划完成时间
    # for activity in activities:
    #     print(f"活动 {activity.name} 的计划完成时间：{activity.skd_erect_finish_date}")

    # 创建项目调度器实例
    scheduler = ProjectScheduler()

    # 获取进度计划
    activities_class, prj_start_date = scheduler.get_schedule('2023-3-10')

    activities_zip = zip(activities, activities_class)
    # 读取 zip 对象中的两个元素
    for activity, activity_class in activities_zip:
        print(
            f"原程序: {activity}, \n新程序: {activity_class}")
        print()

    print(activities==activities_class)

